package com.hera.process.config.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hera.base.util.Constantes;
import com.hera.base.util.FiltroDTO;
import com.hera.process.config.dao.AddTableDetailDao;
import com.hera.process.config.dao.AdditionalTableDao;
import com.hera.process.config.dao.DynaformDao;
import com.hera.process.config.dao.DynaformFieldDao;
import com.hera.process.config.dao.DynaformStageDao;
import com.hera.process.config.dao.FormFieldDao;
import com.hera.process.config.dao.ProcessDao;
import com.hera.process.config.dao.StageDao;
import com.hera.process.config.dao.StageRollupDao;
import com.hera.process.config.dao.StageUserDao;
import com.hera.process.config.dao.SwimlaneDao;
import com.hera.process.config.domain.ParameterDetail;
import com.hera.process.config.service.ProcessService;
import com.hera.process.external.domain.AddTableDetail;
import com.hera.process.external.domain.AdditionalTable;
import com.hera.process.form.domain.Dynaform;
import com.hera.process.form.domain.DynaformField;
import com.hera.process.form.domain.DynaformStage;
import com.hera.process.form.domain.FormField;
import com.hera.process.workflow.domain.Process;
import com.hera.process.workflow.domain.Stage;
import com.hera.process.workflow.domain.StageRollup;
import com.hera.process.workflow.domain.StageUser;
import com.hera.process.workflow.domain.Swimlane;
import com.hera.process.workflow.domain.SwimlanePK;

@Service("processservice")
public class ProcessServiceImpl implements ProcessService {

	static final Logger logger = Logger.getLogger(UserServiceImpl.class);

	
	@Autowired
	private ProcessDao processdao;
	
	@Autowired
	private SwimlaneDao swimlaneDao;
	
	@Autowired
	private AdditionalTableDao addtableDao;
	
	@Autowired
	private AddTableDetailDao addtabledetailDao;
	
	@Autowired
	private DynaformDao dynaformDao;
	
	@Autowired
	private FormFieldDao formfieldDao;
	
	@Autowired
	private StageDao stageDao;
	
	@Autowired
	private StageRollupDao stageRollupDao;
	
	@Autowired
	private DynaformStageDao dynaformStageDao;
	
	@Autowired
	private StageUserDao stageUserDao;
	
	@Autowired
	private DynaformFieldDao dynaformfielddao;
	@Override
	public Process findById(Long id) {
		
		return processdao.findOne(id);
	}

	@Override
	public void update(Process entity) {
		
		processdao.save(entity);
	}

	@Override
	public void save(Process entity) {
		entity.setFlagDelete(false);
		processdao.save(entity);
		
		Swimlane swimlane= new Swimlane();
		swimlane.setSwimlanePK(new SwimlanePK(entity.getIdProcess(),new Long(0)));
		swimlane.setDescription("Swinlane");
		swimlaneDao.save(swimlane);
	}

	@Override
	public void delete(Process entity) {
		

//		swimlaneDao.deleteByProcess(entity.getIdProcess());
//		LLAMAMOS A LA ENTIDAD DEL BEAN CON ESTADO TRUE
		entity.setFlagDelete(true);
		processdao.save(entity);
	}

	@Override
	public List<Process> findAll() {
		
		return processdao.findAll();
	}

	@Override
	public List<Process> findAll(FiltroDTO filtroDTO) {
		
		return processdao.obtenerEntidadesPorFiltros(filtroDTO);
	}

	public boolean validarProceso(Process process){
		//Realizamos las validaciones:
		//1. tiene que haber un stage tipo inicio = 100, como minimo uno de fin = 101 y un minimo un stage = 103
		//2. Las transiciones stage_rollup tienen que estar conectadas (validacion id_stage y id_stage_next)
		
		
		boolean hasBegin = false;
		boolean hasEnd = false;
		boolean hasStage = false;
		List<Stage> listStage = stageDao.findStageByProcessId(process.getIdProcess());
		
		for(int i=0; i<listStage.size(); i++){
			if(Constantes.NODETYPE.ID_BEGIN.equals(listStage.get(i).getIdType())){
				hasBegin = true;
				break;
			}
		}
		if(!hasBegin){
			throw new RuntimeException("Es nececesario un Stage Inicial");
		}
		
		for(int i=0; i<listStage.size(); i++){
			if(Constantes.NODETYPE.ID_END.equals(listStage.get(i).getIdType())){
				hasEnd = true;
				break;
			}
		}
		if(!hasEnd){
			throw new RuntimeException("Es nececesario un Stage Final");
		}
		for(int i=0; i<listStage.size(); i++){
			if(Constantes.NODETYPE.ID_STAGE.equals(listStage.get(i).getIdType())){
				hasStage = true;
				break;
			}
		}
		if(!hasStage){
			throw new RuntimeException("Es nececesario un Stage como mínimo");
		}
		
		boolean isTransitionIncorrect = false;
		List<StageRollup> listStageRollup = stageRollupDao.findStageRollupByProcessId(process.getIdProcess());
		
		if(listStageRollup == null || listStageRollup.size() == 0){
			throw new RuntimeException("El proceso no tiene flechas de transición");
		}
		
		for(int i=0; i<listStageRollup.size(); i++){
			if(listStageRollup.get(i).getIdFigureTo() == null){
				isTransitionIncorrect = true;
				break;
			}
		}
		if(isTransitionIncorrect){
			throw new RuntimeException("Todas las Flechas tiene que tener destino asignado");
		}
		
		boolean isStageDisconected = false;
		//Validando que todos los stages esten conectados
		for(int i=0; i<listStage.size(); i++){
			boolean isLocalStageConected = false;
			for(int j=0; j<listStageRollup.size(); j++){
				if(listStage.get(i).getIdStage().equals(listStageRollup.get(j).getStageFrom().getIdStage())){
					isLocalStageConected = true;
					break;
				}
				if(listStage.get(i).getIdStage().equals(listStageRollup.get(j).getStageTo().getIdStage())){
					isLocalStageConected = true;
					break;
				}
			}
			if(!isLocalStageConected){
				isStageDisconected = true;
				break;
			}
		}
		if(isStageDisconected){
			throw new RuntimeException("Todos los Stages deben estar conectados");
		}
		
		//3. todos los stages de tipo inicio y stage 100 y 101 tienen que tener asociado un formulario; un registro en la tabla dynaform_stage		
		
		List<DynaformStage> listDynaformStage = dynaformStageDao.findByIdProcess(process.getIdProcess());
		
		boolean notHasForm = false;
		for(int i=0; i<listDynaformStage.size(); i++){
			boolean notHasFormLocal = false;
			for(int j=0; j<listStage.size(); j++){
				if(Constantes.NODETYPE.ID_BEGIN.equals(listStage.get(j).getIdType())
						|| Constantes.NODETYPE.ID_STAGE.equals(listStage.get(j).getIdType()) ){
					if(listStage.get(j).getIdStage().equals(listDynaformStage.get(i).getStage().getIdStage())){
						notHasFormLocal = true;
						break;
					}
				}
			}
			if(!notHasFormLocal){
				notHasForm =  true;
				break;
			}
		}
		if(notHasForm){
			throw new RuntimeException("Todos los Stages tienen que tener un Formulario asignado");
		}
		
		//4. todos los stages de tipo inicio y stage 100 y 101 tienen que tener asignado un usuario o grupo de usuarios; un registro en la tabla stage_user
		List<StageUser> listStageUser = stageUserDao.findByStageIdProcess(process.getIdProcess());
		boolean notHasUser = false;
		for(int i=0; i<listStageUser.size(); i++){
			boolean notHasUserLocal = false;
			for(int j=0; j<listStage.size(); j++){
				if(Constantes.NODETYPE.ID_BEGIN.equals(listStage.get(j).getIdType())
						|| Constantes.NODETYPE.ID_STAGE.equals(listStage.get(j).getIdType()) ){
					if(listStage.get(j).getIdStage().equals(listStageUser.get(i).getStage().getIdStage())){
						notHasUserLocal = true;
						break;
					}
				}
			}
			if(!notHasUserLocal){
				notHasUser =  true;
				break;
			}
		}
		
		if(notHasUser){
			throw new RuntimeException("Todos los Stages tienen que tener un usuario o grupo de usuarios asignados");
		}
		
		//5. Todos los formularios tienen que tener como minimo un field.
		//TODO Pendiente acomodar de acuerdo al nuevo modelo
		
		return true;
	}
	
	@Override
	public void publicarProceso(Process process) {
		//1.obtenemos los formfield del proceso publicar
		//1 tabla generica por proceso
		
		AdditionalTable additionalTable= new AdditionalTable();
		additionalTable.setProcess(process);
		additionalTable.setIdType(Constantes.TIPO_TABLA.TABLA_BASE);
		additionalTable.setName(Constantes.STRINGVALUES.NOMBRETABLAGENERICO+process.getIdProcess());
		addtableDao.save(additionalTable);
		
		//TODO 4. actualizar en la tabla "dynaform" el id del registro generado
		//en la tabla "additional_table"
		FiltroDTO filtro6= new FiltroDTO();
		filtro6.addParametro("idProcess", process.getIdProcess());
		filtro6.addParametro("type.idParameterDetail", Constantes.FORMTYPE.NORMAL);
		List<Dynaform> dyna1=  dynaformDao.obtenerEntidadesPorFiltros(filtro6);
		for (Dynaform dynaform : dyna1) {
			dynaform.setAdditionalTable(additionalTable);
			dynaformDao.save(dynaform);
		}
		
		List<FormField> listformfield = formfieldDao.findAllFormFieldInProcess(Constantes.FORMTYPECOMPONENT.TABLE, process.getIdProcess());
		for(int i=0; i<listformfield.size(); i++){
			//6. por cada campo registrar en la tabla "formfield"
			AddTableDetail addtabledetail= new AddTableDetail();	
			addtabledetail.setAdditionalTable(additionalTable);
			addtabledetail.setName(listformfield.get(i).getName());
			//addtabledetail.setLength(listformfield.get(i).getMaxLength());
			if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.CHECKBOX)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.CHAR));
				addtabledetail.setLength(1);
			}else if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.COMBOBOX)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.VARCHAR));
				addtabledetail.setLength(25);
			}else if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.INPUTDATE)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.DATE));
				addtabledetail.setLength(6);
			}else if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.OPTIONGROUP)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.VARCHAR));
				addtabledetail.setLength(25);
			}else if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.TEXTFIELD)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.VARCHAR));
				addtabledetail.setLength(listformfield.get(i).getMaxLength());
			}else if(listformfield.get(i).getIdType().equals(Constantes.FORMTYPECOMPONENT.TEXTAREA)){
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.VARCHAR));
				addtabledetail.setLength(listformfield.get(i).getMaxLength());
			}
			//Agregar los demas componentes
			addtabledetailDao.save(addtabledetail);
			
			//Asociando el addtabledetail al formfield del formulario
			listformfield.get(i).setAddTableDetail(addtabledetail);				
			formfieldDao.save(listformfield.get(i));
		}

		//Para todos los formularios de tipo normal
		FiltroDTO filtro5= new FiltroDTO();
		filtro5.addParametro("idProcess", process.getIdProcess());
		filtro5.addParametro("type.idParameterDetail", Constantes.FORMTYPE.NORMAL);
		List<Dynaform> dynaNormal=  dynaformDao.obtenerEntidadesPorFiltros(filtro5);
		for (Dynaform dynaform : dynaNormal) {	
			//Actualizar en la tabla "dynaform" el id del registro generado
			//en la tabla "additional_table"
			dynaform.setAdditionalTable(additionalTable);			
			dynaformDao.save(dynaform);
		}
		
		//2. por cada formulario tipo grid registra en la tabla addition_table
		FiltroDTO filtro3= new FiltroDTO();
		filtro3.addParametro("idProcess", process.getIdProcess());
		filtro3.addParametro("type.idParameterDetail", Constantes.FORMTYPE.GRID);
		List<Dynaform> dyna=  dynaformDao.obtenerEntidadesPorFiltros(filtro3);
		for (Dynaform dynaform : dyna) {			
			AdditionalTable additionalTable1 = new AdditionalTable();
			additionalTable1.setProcess(process);
			additionalTable1.setIdType(Constantes.TIPO_TABLA.TABLA_DETALLE);
			additionalTable1.setName(Constantes.STRINGVALUES.NOMBRETABLAGENERICO + process.getIdProcess() + 
					Constantes.STRINGVALUES.NOMBRETABLADETGENERICO + dynaform.getIdDynaform());
			
			//3. registra en la tabla additional_table
			addtableDao.save(additionalTable1);
			
			//4. actualizar en la tabla "dynaform" el id del registro generado
			//en la tabla "additional_table"
			dynaform.setAdditionalTable(additionalTable1);			
			dynaformDao.save(dynaform);
			
			FiltroDTO filtro4= new FiltroDTO();
			filtro4.addParametro("dynaformFieldPK.idDynaform", dynaform.getIdDynaform());			
			List<DynaformField> details1=dynaformfielddao.obtenerEntidadesPorFiltros(filtro4);
			
			for (DynaformField dynaformfield : details1) {
				//6. por cada campo registrar en la tabla "formfield"
				AddTableDetail addtabledetail= new AddTableDetail();	
				addtabledetail.setAdditionalTable(additionalTable1);
				addtabledetail.setName(dynaformfield.getField().getName());
				addtabledetail.setLength(dynaformfield.getLength());
				addtabledetail.setDatatype(new ParameterDetail(Constantes.TIPODATO.VARCHAR));
				addtabledetailDao.save(addtabledetail);
				//7.actualizar en la tabla "formfield" el id del registro
				//generado en la tabla "add_detail_table"
				dynaformfield.getField().setAddTableDetail(addtabledetail);				
				formfieldDao.save(dynaformfield.getField());
			}
			
			
		}
			
		
		
		//System.out.println("proceso ......"+"codigo: "+process.getIdProcess()+" nombre :"+process.getName());
		FiltroDTO filtro= new FiltroDTO();
		filtro.addParametro("process.idProcess", process.getIdProcess());
		filtro.addOrdenamientoAscendente("type.idParameterDetail");
		List<AdditionalTable> listAdditionalTable = addtableDao.obtenerEntidadesPorFiltros(filtro);
		String nombreSchema = process.getDatasource().getSchema();
		String nombretablabase = "";
		for(int j=0; j<listAdditionalTable.size(); j++){
			AdditionalTable table = listAdditionalTable.get(j);
			FiltroDTO filtro2 = new FiltroDTO();
			filtro2.addParametro("additionalTable.idAdditionalTable", table.getIdAdditionalTable());
			List<AddTableDetail> details = addtabledetailDao.obtenerEntidadesPorFiltros(filtro2);
			
			String sql="";					
			sql+="create table "+nombreSchema+"."+table.getName()+"(";
			sql+="id number(19) not null, ";
			for (int i = 0; i < details.size(); i++) {
				AddTableDetail tableDetail = details.get(i);
				if(Constantes.TIPODATO.VARCHAR.equals(tableDetail.getDatatype().getIdParameterDetail())
						|| Constantes.TIPODATO.CHAR.equals(tableDetail.getDatatype().getIdParameterDetail())
						|| Constantes.TIPODATO.NUMERIC.equals(tableDetail.getDatatype().getIdParameterDetail())
						|| Constantes.TIPODATO.DATE.equals(tableDetail.getDatatype().getIdParameterDetail())){
					
					//sql+=tableDetail.getName()+" "+tableDetail.getDatatype().getName()+"("+tableDetail.getLength()+")"+((details.size()-1==i)?"":",");
					sql+=tableDetail.getName()+" "+tableDetail.getDatatype().getName()+"("+(tableDetail.getLength()==null?"20":tableDetail.getLength())+"), ";
					
				}
				/*else if(Constantes.TIPODATO.DATE.equals(tableDetail.getDatatype().getIdParameterDetail())){
					sql+=tableDetail.getName()+" "+tableDetail.getDatatype().getName()+((details.size()-1==i)?"":",");					
				}*/
			}
			if(table.getType().getIdParameterDetail().equals(Constantes.TIPO_TABLA.TABLA_DETALLE)){
				sql+="id_parent  number(19),";
			}
			sql+="user_created  VARCHAR2(30), ";
			sql+="date_created  TIMESTAMP(6), ";
			sql+="user_modified VARCHAR2(30), ";
			sql+="date_modified TIMESTAMP(6)";
			
			sql+=")";
			logger.debug(sql);
			String sqlAlterPK="alter table "+nombreSchema+"."+table.getName()+" add constraint PK_"+table.getName()+" primary key (ID)";
			String sqlIndex="CREATE SEQUENCE "+nombreSchema+".SEQ_"+table.getName()+" minvalue 1 maxvalue 9999999999 start with 1 increment by 1 nocache";
			
			List<Object> params = new ArrayList<Object>();
			params.add(sql);
			params.add(sqlAlterPK);
			params.add(sqlIndex);
			if(table.getType().getIdParameterDetail().equals(Constantes.TIPO_TABLA.TABLA_DETALLE)){
				String sqlAlterFK="alter table "+nombreSchema+"."+table.getName()+" add constraint FK_" + table.getName() + " foreign key (id_parent) references "+nombreSchema+"."+ nombretablabase +"(id)";
				params.add(sqlAlterFK);
				processdao.ejecutarUpdateNativeSQL("call WORKFLOW.pkg_external.create_ext_table_detail (?,?,?,?)", params);
			}else if(table.getType().getIdParameterDetail().equals(Constantes.TIPO_TABLA.TABLA_BASE)){
				nombretablabase = table.getName();
				processdao.ejecutarUpdateNativeSQL("call WORKFLOW.pkg_external.create_ext_table (?,?,?)", params);
			}
			
		}
	}
	
	@Override
	public List<FormField> findAllFormFieldInProcess(Process process){
		return formfieldDao.findAllFormFieldInProcess(Constantes.FORMTYPECOMPONENT.TABLE, process.getIdProcess());
	}

}
